Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

services/horizon: Fix ReapLookupTables query #4525

Merged
merged 15 commits into from
Aug 11, 2022

Conversation

bartekn
Copy link
Contributor

@bartekn bartekn commented Aug 9, 2022

This commit fixes the query added in ee063a7. The previous query was using limit ... offset ... which becomes slow for larger offset values. This is happening because (from docs[1]):

The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient.

I rewrote the query to use id field in tables in where id > .... Before each run, the new id offset is fetched from the table that is later returned to be used in the next cycle.

I also realized that rewriting count(*) in subqueries to 1 with limit 1 further improves the query performance. This is because instead of counting all rows in parent tables it just check if there are any rows in tables (what we need to determine if the row is orphaned or not). This allows running the reaper also for history_accounts and history_assets tables.

[1] https://www.postgresql.org/docs/current/queries-limit.html

@bartekn bartekn marked this pull request as ready for review August 9, 2022 14:54
@bartekn bartekn requested a review from a team August 9, 2022 14:54
Copy link
Contributor

@Shaptic Shaptic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a general question about the way reaping works, as that isn't part of the codebase I have had much exposure to yet.

The docstring on ReapLookupTables states that it

removes rows from lookup tables which aren't used (orphaned), i.e. history entries for them were reaped

To me, the bigger picture of reaping entries has to do with ledger advancement, right? i.e. you set HISTORY_RETENTION_COUNT=x means data from x ledgers get kept around. If every table either (a) tracks the ledger a row applies to or (b) can somehow be joined to identify said ledger, isn't reaping then just a matter of DELETE FROM table WHERE ledgerSeq < latestIngestedLedgerSeq; for every table?

In a related vein, it'd be nice if this PR clarified what offsets means within the docstring of ReapLookupTables. (To be clear, I'm aware of their purpose thanks to the awesome description in #4518, but adding it inline to the code will be helpful I think!)

@@ -935,7 +957,7 @@ func constructReapLookupTablesQuery(table string, historyTables []tableObjectFie
for i, historyTable := range historyTables {
_, err = fmt.Fprintf(
&sb,
`(select count(*) from %s where %s = hcb.id) as c%d, `,
`(select 1 from %s where %s = hcb.id limit 1) as c%d, `,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice optimization 👍

"(select 1 from history_trades where base_account_id = hcb.id limit 1) as c2, "+
"(select 1 from history_trades where counter_account_id = hcb.id limit 1) as c3, "+
"(select 1 from history_transaction_participants where history_account_id = hcb.id limit 1) as c4, "+
"1 as cx from history_accounts hcb where id >= 0 order by id limit 10) as sub "+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are negative ids possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the 0 in id >=0 condition is the offset param and it's there just to ensure we move forward when iterating over the tables in batches. So for next cycle it will be id >=1000 if the ID after 1000 rows (batch size) is equal 1000.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could probably clean this up with WITH clauses, and named sub-queries?

@bartekn
Copy link
Contributor Author

bartekn commented Aug 10, 2022

To me, the bigger picture of reaping entries has to do with ledger advancement, right? i.e. you set HISTORY_RETENTION_COUNT=x means data from x ledgers get kept around. If every table either (a) tracks the ledger a row applies to or (b) can somehow be joined to identify said ledger, isn't reaping then just a matter of DELETE FROM table WHERE ledgerSeq < latestIngestedLedgerSeq; for every table?

This is exactly what services/horizon/internal/reap does: it removes all rows from history tables which contains transactions, operations, effects, etc. before a specified ledger (current sequence — retention count). However, we also have lookup tables that denormalize history tables to save space. They look like [id, name] where name is account ID in history_accounts or claimable balance ID in history_claimable_balances. And then when used in history tables like history_operation_participants we can use an integer id instead of a long account ID.

The obvious problem with lookup tables is that a given ID can be used in multiple ledger ranges (depends on activity of an account or claimable balance). So we can't just simply remove rows from lookup tables while removing rows from normal history tables. Second, lookup tables are actively used by ingestion so if reaper removes a lookup row after ingestion loaded it it can cause inconsistencies in the DB.

@bartekn
Copy link
Contributor Author

bartekn commented Aug 10, 2022

@stellar/horizon-committers I added two more things, PTAL:

  • 8c2e40c Logging deleted rows counter (only when any rows removed).
  • 0317d5a Activates lookup reaping feature only when --history-retention-count is set.

@bartekn bartekn merged commit 69225cf into stellar:master Aug 11, 2022
@bartekn bartekn deleted the fix-reap-lookup-tables-query branch August 11, 2022 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants